"""
告警通知系统

实现错误告警、完成通知、状态报告等功能。
"""

import time
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from typing import Dict, List, Optional, Any, Callable
from dataclasses import dataclass
from datetime import datetime, timezone
from enum import Enum

from ..utils.logger import get_module_logger

logger = get_module_logger(__name__)


class AlertLevel(Enum):
    """告警级别"""
    INFO = "info"
    WARNING = "warning"
    ERROR = "error"
    CRITICAL = "critical"


class AlertChannel(Enum):
    """告警渠道"""
    EMAIL = "email"
    WEBHOOK = "webhook"
    LOG = "log"


@dataclass
class AlertRule:
    """告警规则"""
    name: str
    condition: Callable[[Dict[str, Any]], bool]
    level: AlertLevel
    channels: List[AlertChannel]
    message_template: str
    cooldown_seconds: int = 300  # 5分钟冷却期
    enabled: bool = True


@dataclass
class Alert:
    """告警信息"""
    rule_name: str
    level: AlertLevel
    message: str
    timestamp: datetime
    context: Dict[str, Any]
    resolved: bool = False


class AlertManager:
    """告警管理器
    
    管理告警规则、发送告警通知。
    """
    
    def __init__(self, config: Optional[Dict[str, Any]] = None):
        """初始化告警管理器
        
        Args:
            config: 告警配置
        """
        self.config = config or {}
        self.rules: Dict[str, AlertRule] = {}
        self.alert_history: List[Alert] = []
        self.last_alert_times: Dict[str, float] = {}
        
        # 邮件配置
        self.email_config = self.config.get('email', {})
        
        # Webhook配置
        self.webhook_config = self.config.get('webhook', {})
        
        # 注册默认规则
        self._register_default_rules()
        
        logger.info("告警管理器初始化完成")
    
    def add_rule(self, rule: AlertRule):
        """添加告警规则
        
        Args:
            rule: 告警规则
        """
        self.rules[rule.name] = rule
        logger.info(f"添加告警规则: {rule.name}")
    
    def remove_rule(self, rule_name: str):
        """移除告警规则
        
        Args:
            rule_name: 规则名称
        """
        if rule_name in self.rules:
            del self.rules[rule_name]
            logger.info(f"移除告警规则: {rule_name}")
    
    def check_alerts(self, context: Dict[str, Any]):
        """检查告警条件
        
        Args:
            context: 检查上下文
        """
        current_time = time.time()
        
        for rule_name, rule in self.rules.items():
            if not rule.enabled:
                continue
            
            try:
                # 检查冷却期
                last_alert_time = self.last_alert_times.get(rule_name, 0)
                if current_time - last_alert_time < rule.cooldown_seconds:
                    continue
                
                # 检查告警条件
                if rule.condition(context):
                    # 生成告警
                    alert = self._create_alert(rule, context)
                    self._send_alert(alert, rule.channels)
                    
                    # 记录告警时间
                    self.last_alert_times[rule_name] = current_time
                    
                    # 添加到历史记录
                    self.alert_history.append(alert)
                    
                    # 保持最近1000条告警记录
                    if len(self.alert_history) > 1000:
                        self.alert_history = self.alert_history[-1000:]
                
            except Exception as e:
                logger.error(f"检查告警规则失败: {rule_name}, {e}")
    
    def send_notification(
        self,
        level: AlertLevel,
        message: str,
        channels: List[AlertChannel],
        context: Optional[Dict[str, Any]] = None
    ):
        """发送通知
        
        Args:
            level: 告警级别
            message: 消息内容
            channels: 通知渠道
            context: 上下文信息
        """
        alert = Alert(
            rule_name="manual",
            level=level,
            message=message,
            timestamp=datetime.now(timezone.utc),
            context=context or {}
        )
        
        self._send_alert(alert, channels)
        self.alert_history.append(alert)
    
    def get_alert_history(self, hours: int = 24) -> List[Alert]:
        """获取告警历史
        
        Args:
            hours: 历史时间范围（小时）
            
        Returns:
            告警历史列表
        """
        cutoff_time = datetime.now(timezone.utc).timestamp() - (hours * 3600)
        return [
            alert for alert in self.alert_history
            if alert.timestamp.timestamp() >= cutoff_time
        ]
    
    def get_alert_summary(self, hours: int = 24) -> Dict[str, Any]:
        """获取告警摘要
        
        Args:
            hours: 统计时间范围（小时）
            
        Returns:
            告警摘要
        """
        history = self.get_alert_history(hours)
        
        # 按级别统计
        level_counts = {}
        for level in AlertLevel:
            level_counts[level.value] = len([a for a in history if a.level == level])
        
        # 按规则统计
        rule_counts = {}
        for alert in history:
            rule_counts[alert.rule_name] = rule_counts.get(alert.rule_name, 0) + 1
        
        return {
            'time_range_hours': hours,
            'total_alerts': len(history),
            'level_distribution': level_counts,
            'rule_distribution': rule_counts,
            'recent_alerts': history[-10:] if history else []
        }
    
    def _create_alert(self, rule: AlertRule, context: Dict[str, Any]) -> Alert:
        """创建告警
        
        Args:
            rule: 告警规则
            context: 上下文
            
        Returns:
            告警对象
        """
        # 格式化消息
        try:
            message = rule.message_template.format(**context)
        except Exception as e:
            logger.warning(f"格式化告警消息失败: {e}")
            message = rule.message_template
        
        return Alert(
            rule_name=rule.name,
            level=rule.level,
            message=message,
            timestamp=datetime.now(timezone.utc),
            context=context
        )
    
    def _send_alert(self, alert: Alert, channels: List[AlertChannel]):
        """发送告警
        
        Args:
            alert: 告警信息
            channels: 发送渠道
        """
        for channel in channels:
            try:
                if channel == AlertChannel.EMAIL:
                    self._send_email_alert(alert)
                elif channel == AlertChannel.WEBHOOK:
                    self._send_webhook_alert(alert)
                elif channel == AlertChannel.LOG:
                    self._send_log_alert(alert)
                
            except Exception as e:
                logger.error(f"发送告警失败: {channel.value}, {e}")
    
    def _send_email_alert(self, alert: Alert):
        """发送邮件告警"""
        if not self.email_config:
            return
        
        smtp_server = self.email_config.get('smtp_server')
        smtp_port = self.email_config.get('smtp_port', 587)
        username = self.email_config.get('username')
        password = self.email_config.get('password')
        from_addr = self.email_config.get('from_addr')
        to_addrs = self.email_config.get('to_addrs', [])
        
        if not all([smtp_server, username, password, from_addr, to_addrs]):
            logger.warning("邮件配置不完整，跳过邮件告警")
            return
        
        # 创建邮件
        msg = MIMEMultipart()
        msg['From'] = from_addr
        msg['To'] = ', '.join(to_addrs)
        msg['Subject'] = f"[{alert.level.value.upper()}] ES Archive Alert: {alert.rule_name}"
        
        # 邮件正文
        body = f"""
告警时间: {alert.timestamp.strftime('%Y-%m-%d %H:%M:%S UTC')}
告警级别: {alert.level.value.upper()}
告警规则: {alert.rule_name}
告警消息: {alert.message}

上下文信息:
{self._format_context(alert.context)}
        """
        
        msg.attach(MIMEText(body, 'plain'))
        
        # 发送邮件
        with smtplib.SMTP(smtp_server, smtp_port) as server:
            server.starttls()
            server.login(username, password)
            server.send_message(msg)
        
        logger.info(f"邮件告警已发送: {alert.rule_name}")
    
    def _send_webhook_alert(self, alert: Alert):
        """发送Webhook告警"""
        if not self.webhook_config:
            return
        
        webhook_url = self.webhook_config.get('url')
        if not webhook_url:
            return
        
        import requests
        
        payload = {
            'timestamp': alert.timestamp.isoformat(),
            'level': alert.level.value,
            'rule_name': alert.rule_name,
            'message': alert.message,
            'context': alert.context
        }
        
        headers = self.webhook_config.get('headers', {})
        timeout = self.webhook_config.get('timeout', 10)
        
        response = requests.post(
            webhook_url,
            json=payload,
            headers=headers,
            timeout=timeout
        )
        response.raise_for_status()
        
        logger.info(f"Webhook告警已发送: {alert.rule_name}")
    
    def _send_log_alert(self, alert: Alert):
        """发送日志告警"""
        log_message = f"ALERT [{alert.level.value.upper()}] {alert.rule_name}: {alert.message}"
        
        if alert.level == AlertLevel.CRITICAL:
            logger.critical(log_message)
        elif alert.level == AlertLevel.ERROR:
            logger.error(log_message)
        elif alert.level == AlertLevel.WARNING:
            logger.warning(log_message)
        else:
            logger.info(log_message)
    
    def _format_context(self, context: Dict[str, Any]) -> str:
        """格式化上下文信息"""
        lines = []
        for key, value in context.items():
            lines.append(f"  {key}: {value}")
        return '\n'.join(lines) if lines else "  无"
    
    def _register_default_rules(self):
        """注册默认告警规则"""
        # 备份失败告警
        self.add_rule(AlertRule(
            name="backup_failed",
            condition=lambda ctx: ctx.get('backup_status') == 'failed',
            level=AlertLevel.ERROR,
            channels=[AlertChannel.EMAIL, AlertChannel.LOG],
            message_template="备份失败: {backup_id}, 错误: {error_message}",
            cooldown_seconds=300
        ))
        
        # 恢复失败告警
        self.add_rule(AlertRule(
            name="restore_failed",
            condition=lambda ctx: ctx.get('restore_status') == 'failed',
            level=AlertLevel.ERROR,
            channels=[AlertChannel.EMAIL, AlertChannel.LOG],
            message_template="恢复失败: {restore_id}, 错误: {error_message}",
            cooldown_seconds=300
        ))
        
        # 高CPU使用率告警
        self.add_rule(AlertRule(
            name="high_cpu_usage",
            condition=lambda ctx: ctx.get('cpu_percent', 0) > 90,
            level=AlertLevel.WARNING,
            channels=[AlertChannel.LOG],
            message_template="CPU使用率过高: {cpu_percent:.1f}%",
            cooldown_seconds=600
        ))
        
        # 高内存使用率告警
        self.add_rule(AlertRule(
            name="high_memory_usage",
            condition=lambda ctx: ctx.get('memory_percent', 0) > 90,
            level=AlertLevel.WARNING,
            channels=[AlertChannel.LOG],
            message_template="内存使用率过高: {memory_percent:.1f}%",
            cooldown_seconds=600
        ))
        
        # 磁盘空间不足告警
        self.add_rule(AlertRule(
            name="low_disk_space",
            condition=lambda ctx: ctx.get('disk_usage_percent', 0) > 90,
            level=AlertLevel.CRITICAL,
            channels=[AlertChannel.EMAIL, AlertChannel.LOG],
            message_template="磁盘空间不足: {disk_usage_percent:.1f}%",
            cooldown_seconds=1800
        ))
